/*
第二版
加入了形状匹配 处理单个视频

输出两个视频 展示背景消除用
2.14 对静止或者变形缓慢的物体进一步处理 但是还没有测试变形的matchshapes阈值
*/

#include <iostream>
#include <sstream>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/videoio.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/video.hpp>
#include "func.h"
const char* params
    = "{ help h         |           | Print usage }"
      "{ input          |           | Path to a video or a sequence of image }"
      "{ dirinput       |           | dir Path to a video or a sequence of image }"
      "{ show           |           | show or not  }"
      "{ output         |           | output path }"
      "{ lr             | -1        | learning rate of MOG2 }"
      "{ algo           | KNN       | Background subtraction method (KNN, MOG2) }";
int main(int argc, char* argv[])
{
    cv::CommandLineParser parser(argc, argv, params);
    parser.about( "This program shows how to use background subtraction methods provided by "
                  " OpenCV. You can process both videos and images.\n" );
    if (parser.has("help"))
    {
        //print help information
        parser.printMessage();
		return 0;    
	}
    //create Background Subtractor objects
	std::vector<std::string> files;
    if (parser.has("dirinput")){
		getFileNames( parser.get<cv::String>("dirinput")  , files);
		for(int i=0;i < files.size(); ++i){
			files[i] = parser.get<cv::String>("dirinput") +"/"+ files[i];
		}
	}
	if (parser.has("input")){
		files.push_back(parser.get<cv::String>("input") );
	}
    for (int i = 0; i < files.size(); ++i){
    	std::string filePath = files[i];
		std::cout<<"processing "<<files[i]<<std::endl;
	    cv::Ptr<cv::BackgroundSubtractor> pBackSub;
    	if (parser.get<cv::String>("algo") == "MOG2")
        	pBackSub = cv::createBackgroundSubtractorMOG2();
    	else
        	pBackSub = cv::createBackgroundSubtractorKNN();
	    cv::VideoCapture capture(filePath);
        if (!capture.isOpened()){
            //error in opening the video input
            std::cerr << "Unable to open: " << parser.get<cv::String>("input") << std::endl;
            return 0;
        }
		cv::Mat frame, fgMask;
		cv::VideoWriter videoWriter;
	    int width =(int)capture.get(cv::CAP_PROP_FRAME_WIDTH);
	    int height = (int)capture.get(cv::CAP_PROP_FRAME_HEIGHT);
        cv::Size S = cv::Size(2*(int)capture.get(cv::CAP_PROP_FRAME_WIDTH),(int)capture.get(cv::CAP_PROP_FRAME_HEIGHT));
        int fps = capture.get(cv::CAP_PROP_FPS);
		if (parser.has("output")){
			std::string::size_type iPos = filePath.find_last_of('\\') + 1;
			std::string filename = files[i].substr(iPos, files[i].length() - iPos);
			filename = filename.substr(0, filename.rfind("."));
			videoWriter.open(parser.get<cv::String>("output")+"/OUT_"+filename+".avi", cv::VideoWriter::fourcc('M','J','P','G'),fps/2 ,S);
		}        
		std::vector<std::vector<cv::Point>> last_contours;
		std::vector<std::pair<double,double>>last_position;
		bool flag = 1;
        while (true) {
            capture >> frame;
            if (frame.empty())
                break;
			flag^=1;
			if(flag)continue;//每两帧检测一次，增加速度的同时可以避免插帧造成的误差
            //update the background model
       	    if (parser.get<cv::String>("algo") == "MOG2")
                pBackSub->apply(frame, fgMask,parser.get<double>("lr"));
            else
                pBackSub->apply(frame, fgMask);
            //get the frame number and write it on the current frame
            //rectangle(frame, cv::Point(10, 2), cv::Point(100,20),
            //          cv::Scalar(255,255,255), -1);
            //std::stringstream ss;
            //ss << capture.get(cv::CAP_PROP_POS_FRAMES);
            //std::string frameNumberString = ss.str();
            //putText(frame, frameNumberString.c_str(), cv::Point(15, 15),
            //        cv::FONT_HERSHEY_SIMPLEX, 0.5 , cv::Scalar(0,0,0));
            //show the current frame and the fg masks
			cv::Mat resultImg =cv::Mat(height, 2*width, CV_8UC3, cv::Scalar::all(0));
			cv::Mat ROI_1 = resultImg(cv::Rect(0, 0, width, height));
			cv::Mat ROI_2 = resultImg(cv::Rect(width, 0, width, height)); 
            //imshow("Frame", frame);
            //imshow("FG Mask", fgMask);
  	        //第一个框图是原图
			frame.copyTo(ROI_1);
			

  	        //第2个框图是背景消除之后标注
			cv::Mat fgMask3Channels = convertTo3Channels(fgMask);
			cv::Mat fgMask_tmp=fgMask;
			std::vector<std::vector<cv::Point>> contours;
            findContours(fgMask_tmp, contours, cv::RETR_EXTERNAL,cv::CHAIN_APPROX_SIMPLE);
        	for (int i = 0; i < contours.size(); ++i)
        	{
          	//判断轮廓大小，当大于1000时进入
            	if(cv::contourArea(contours[i])>50)
            	{
                //获取轮廓矩形
                	cv::Rect rect=cv::boundingRect(contours[i]);
                //在矩形的左上角点输出文字
              	    cv::putText(fgMask_tmp, std::to_string(contourArea(contours[i])), cv::Point(rect.x,rect.y),
                	    cv::FONT_HERSHEY_PLAIN, 0, cv::Scalar(255, 255, 255));
            	}
        	}
            fgMask3Channels = convertTo3Channels(fgMask_tmp);
			fgMask3Channels.copyTo(ROI_2);	
			

			if (parser.has("output")){
				videoWriter.write(resultImg);
            }
			if (parser.has("show")){
				cv::imshow("image",resultImg);
			}
			//get the input from the keyboard
            int keyboard = cv::waitKey(1);
            if (keyboard == 'q' || keyboard == 27)
                break;
        }
		
		if (parser.has("output")){
			videoWriter.release();
		}
	}
        return 0;
}
